% $Id: parse_xml.m 11 2008-11-08 14:48:39Z Jie.Bao $ 

function result = parse_xml(filename, result)
%PARSE_XML reads log files of XML format and converts them into MATLAB's 
%          struct tree
%
% Usage: result = parse_xml(filename, result)
%
% Input: filename   name of the log file
%        result     a MATLAB structure that contains the result after 
%                   convertion and is passed to the parsing of the next 
%                   log file
%



% force MATLAB to use the double precision in the display of numbers
format long;

% the function xml_read is part of the XML I/O Toolbox, which reads an
% XML file and saves the data to a MATLAB sturcture
s = xml_read(filename); 

% log is the new MATLAB structure after parsing the log file and is stored
% in result, entry_counter is to keep record of the global id of an event, 
% namely its primary key
log = result.log;
counter = result.entry_counter;

% map_counter is the numbering of the 'studies map - map removed' pairs, 
% detour_counter is the numbering of the 'left route - back on route' pairs, 
% and standing_counter is the numbering of the standing-walking pairs, 
% all initialized to 1 at the beginning of each log file
map_counter = 1;
detour_counter = 1;
standing_counter = 1;

% Detour marker and flags for the 3 markers. There appears an error 
% message if the pairs are incomplete. Default is 0 for all.
detour = 0;
start_map = 0; end_map = 0;
start_detour = 0; end_detour = 0;
start_standing = 0; end_standing = 0;

% initialize indices for the event CBA, CBB & CBC
idx_A = 0; idx_B = 0; idx_C = 0;

% vector to store Time_SD and used in the event sorting afterwards
relative_time = [];

% loop through all events
for i = counter:(counter + length(s.data.event) - 1)
        
    %% header
    % experimenter 1
    log.event(i).ATTRIBUTE.Experimenter_1 = ...
        rename(s.header.ATTRIBUTE.experimenter1, 'other');

    % experimenter 2
    log.event(i).ATTRIBUTE.Experimenter_2 = ...
        rename(s.header.ATTRIBUTE.experimenter2, 'other');

    % add log file number
    log.event(i).ATTRIBUTE.Log_File_Number = s.header.ATTRIBUTE.subject;

    % Exp_Cond
    log.event(i).ATTRIBUTE.Exp_Cond = 'Wegenetzkarte';

    % new date format
    date_num = datenum(s.header.ATTRIBUTE.date(4:end), 'mmm dd yyyy');
    log.event(i).ATTRIBUTE.Date = ...
        rename(datestr(date_num, 'dd-mmm-yyyy'), 'date');

    % path of the design file
    log.event(i).ATTRIBUTE.Design_File = s.header.ATTRIBUTE.design;

    % start time
    log.event(i).ATTRIBUTE.Start_Time = s.header.ATTRIBUTE.starttime;
    
    %% data
    current_event = s.data.event(i - result.entry_counter + 1);
    % event name
    log.event(i).ATTRIBUTE.Event_Name = ...
        rename(current_event.ATTRIBUTE.name, 'other');
    
    % relative time
    log.event(i).ATTRIBUTE.Relative_Time = current_event.ATTRIBUTE.time;
    
    % relative time in seconds
    reference = datevec([s.header.ATTRIBUTE.date(4:end) ' 00:00:00.000'], ...
        'mmm dd yyyy HH:MM:SS.FFF');
    date_vec = datevec([s.header.ATTRIBUTE.date(4:end) ' ' ...
        current_event.ATTRIBUTE.time], 'mmm dd yyyy HH:MM:SS.FFF');
    time_string = sprintf('%.3f', etime(date_vec, reference));
    log.event(i).ATTRIBUTE.Time_SD = rename(time_string, 'number');
    relative_time = [relative_time str2num(time_string)];
    
    % time for ArcGIS
    log.event(i).ATTRIBUTE.Time_ArcGIS = datestr(date_vec, ...
        'yyyy-mm-dd HH:MM:SS');
    
    % x, y position
    if isfield(current_event.ATTRIBUTE, 'pos')
        xy = regexp(current_event.ATTRIBUTE.pos, ',', 'split');
        % The following calculation makes use of the Multiple Precision 
        % Toolbox by creating an mp class to represent numbers and thus increase
        % the number of significant digits. The number, however, is still
        % not accurate to the 12th decimal, which, I believe, is due to
        % the limited representation of floating point numbers at the
        % hardware level
        x = mp(str2num(cell2mat(xy(1)))); 
        y = mp(str2num(cell2mat(xy(2))));
        log.event(i).ATTRIBUTE.Pos_x = rename(sprintf('%6.12f', x), 'number');
        log.event(i).ATTRIBUTE.Pos_y = rename(sprintf('%6.12f', y), 'number');
        new_y = y*(-1088/1762);
        log.event(i).ATTRIBUTE.New_Pos_y = rename(sprintf('%6.12f', new_y), ...
            'number');
        log.event(i).ATTRIBUTE.UTM_x = rename(sprintf('%6.12f', (419.754222685006*x)+...
            (117.396221943025*new_y)+(413962.774184427)), 'number');
        log.event(i).ATTRIBUTE.UTM_y = rename(sprintf('%6.12f', -127.659478089772*x+...
            (420.062135020271*new_y)+(5317370.94557086)), 'number');
    end
    
    % x, y position of object
    if isfield(current_event.ATTRIBUTE, 'object')
        xy = regexp(current_event.ATTRIBUTE.object, ',', 'split');
        x = mp(str2num(cell2mat(xy(1))));
        y = mp(str2num(cell2mat(xy(2))));
        log.event(i).ATTRIBUTE.Pos_Object_x = rename(sprintf('%6.12f', x), ...
            'number');
        log.event(i).ATTRIBUTE.Pos_Object_y = rename(sprintf('%6.12f', y), ...
            'number');
        new_y = y*(-1088/1762);
        log.event(i).ATTRIBUTE.New_Pos_Object_y = rename(sprintf('%6.12f', new_y), ...
            'number');
        log.event(i).ATTRIBUTE.UTM_Object_x = rename(sprintf('%6.12f', ...
            (419.754222685006*x)+(117.396221943025*new_y)+(413962.774184427)), ...
            'number');
        log.event(i).ATTRIBUTE.UTM_Object_y = rename(sprintf('%6.12f', ...
            -127.659478089772*x+(420.062135020271*new_y)+(5317370.94557086)), ...
            'number');
    end
    
    % set map marker
    if findstr(log.event(i).ATTRIBUTE.Event_Name, 'studies_map')
        log.event(i).ATTRIBUTE.Interval_Pairs_Map = [ ...
            sprintf('%06d', s.header.ATTRIBUTE.subject) '_' ...
            sprintf('%06d', map_counter)];
        
        start_map = start_map + 1;
    end
    if findstr(log.event(i).ATTRIBUTE.Event_Name, 'map_removed')
        log.event(i).ATTRIBUTE.Interval_Pairs_Map = [ ...
            sprintf('%06d', s.header.ATTRIBUTE.subject) '_' ...
            sprintf('%06d', map_counter)];
        % after the occurence of 'map removed' event, counter is
        % incremented by 1
        map_counter = map_counter + 1;

        end_map = end_map + 1;
    end
    
    % set detour marker
    if findstr(log.event(i).ATTRIBUTE.Event_Name, 'left_route')
        log.event(i).ATTRIBUTE.Interval_Pairs_Detours = [ ...
            sprintf('%06d', s.header.ATTRIBUTE.subject) '_' ...
            sprintf('%06d', detour_counter)];

        start_detour = start_detour + 1;
    end
    if findstr(log.event(i).ATTRIBUTE.Event_Name, 'back_on_route')
        log.event(i).ATTRIBUTE.Interval_Pairs_Detours = [ ...
            sprintf('%06d', s.header.ATTRIBUTE.subject) '_' ...
            sprintf('%06d', detour_counter)];
        % after the occurence of 'back on route' event, counter is
        % incremented by 1
        detour_counter = detour_counter + 1;

        end_detour = end_detour + 1;
    end
    
    % set standing marker
    if findstr(log.event(i).ATTRIBUTE.Event_Name, 'standing')
        log.event(i).ATTRIBUTE.Interval_Pairs_Standing = [ ...
            sprintf('%06d', s.header.ATTRIBUTE.subject) '_' ...
            sprintf('%06d', standing_counter)];

        start_standing = start_standing + 1;
    end
    if findstr(log.event(i).ATTRIBUTE.Event_Name, 'walking')
        log.event(i).ATTRIBUTE.Interval_Pairs_Standing = [ ...
            sprintf('%06d', s.header.ATTRIBUTE.subject) '_' ...
            sprintf('%06d', standing_counter)];
        % after the occurence of 'walking' event, counter is
        % incremented by 1
        standing_counter = standing_counter + 1;

        end_standing = end_standing + 1;
    end
    
    % keep record of the indices of CBA, CBB & CBC
    if findstr(log.event(i).ATTRIBUTE.Event_Name, 'correct_belief_A')
        idx_A = i;
    elseif findstr(log.event(i).ATTRIBUTE.Event_Name, 'correct_belief_B')
        idx_B = i;
    elseif findstr(log.event(i).ATTRIBUTE.Event_Name, 'correct_belief_C')
        idx_C = i;
    end
    
    % set detour value (correct=0, detour=1)
    log.event(i).ATTRIBUTE.Detours = detour;
    if findstr(log.event(i).ATTRIBUTE.Event_Name, 'left_route')
        detour = 1;
    elseif findstr(log.event(i).ATTRIBUTE.Event_Name, 'back_on_route')
        detour = 0;
        % 'back on route' itself should have a detour value of 0
        log.event(i).ATTRIBUTE.Detours = detour;
    end
    
    % entry counter is incremented by 1
    counter = counter + 1;
end

% check if there are incomplete pairs
if start_map < end_map
    error('Missing ''studies_map'' event(s)!');
elseif start_map > end_map
    error('Missing ''map_removed'' event(s)!');
end

if start_detour < end_detour
    error('Missing ''left_route'' event(s)!');
elseif start_detour > end_detour
    error('Missing ''back_on_route'' event(s)!');
end

if start_standing < end_standing
    error('Missing ''standing'' event(s)!');
elseif start_standing > end_standing
    error('Missing ''walking'' event(s)!');
end

% sort events by Time_SD within each log file
[dummy order] = sort(relative_time);
log.event(result.entry_counter:(result.entry_counter + ...
    length(s.data.event) - 1)) = log.event(order + ...
    result.entry_counter - 1); 
    
% set primary key & area marker
if idx_A
    if findstr(log.event(result.entry_counter).ATTRIBUTE.Event_Name, 'gps')
        area = 0;  % preceding gps-entry
    else 
        area = 1;  % training, non-gps entry
    end
elseif idx_B || idx_C
    if findstr(log.event(result.entry_counter).ATTRIBUTE.Event_Name, 'gps')
        area = 0;  % preceding gps-entry
    else 
        area = 2;  % test, non-gps entry
    end
end
    
for i = result.entry_counter:(result.entry_counter + length(s.data.event) - 1)
    log.event(i).ATTRIBUTE.Primary_Key = i;
    log.event(i).ATTRIBUTE.Area = area;
    switch area 
        case 0
            % if reaches first non-gps entry
            if i <= idx_A && isempty(findstr(log.event(i).ATTRIBUTE.Event_Name, 'gps'))
                area = 1;
                log.event(i).ATTRIBUTE.Area = area;
            elseif (i <= idx_B || i <= idx_C) && ...
                    isempty(findstr(log.event(i).ATTRIBUTE.Event_Name, 'gps'))
                area = 2;
                log.event(i).ATTRIBUTE.Area = area;
            end
        case 1
            % if reaches end of training 
            if i > idx_A && isempty(findstr(log.event(i).ATTRIBUTE.Event_Name, 'gps'))
                area = 2;
                log.event(i).ATTRIBUTE.Area = area;
            elseif i > idx_A
                area = 0;
                log.event(i).ATTRIBUTE.Area = area;
            end
        case 2
            % if reaches end of test
            if idx_C && i > idx_C || ~idx_C && i > idx_B
                area = 0;
                log.event(i).ATTRIBUTE.Area = area;
            end
    end
end

% return the values of log and entry_counter to result
result.log = log;
result.entry_counter = counter;


end


function new_name = rename(old_name, type)
%RENAME rename the events
%
% Usage: new_name = rename(old_name, date)
%
% Input: old_name   old string to be renamed
%        type       type of string, e.g. 'date', 'number' or 'other'

   new_name = regexprep(old_name, ' ', '_');
   new_name = regexprep(new_name, '/', '_or_');
   new_name = regexprep(new_name, 'sj.', 'pt');
   new_name = regexprep(new_name, '_>_', '_');
   % remove the trailing dots
   new_name = regexprep(new_name, '\w*\.\s', new_name(1:end-1));
   if strcmp(type, 'date')
       % german abbreviations of months
       new_name = regexprep(new_name, 'Mar', 'Mrz');
       new_name = regexprep(new_name, 'May', 'Mai');
       new_name = regexprep(new_name, 'Oct', 'Okt');
       new_name = regexprep(new_name, 'Dec', 'Dez');
   elseif strcmp(type, 'number')
       % german decimal separator
       new_name = regexprep(new_name, '\.', ',');
   end
   
end
